using System;
using System.Collections.Generic;
using System.Text;

namespace OurMediaPlayer.Logic.Filters
{
    class ConvolutionFilter : Filter
    {
        public ConvolutionFilter()
        {
            this.Matrix = new int[3, 3] { { 0, 0, 0 }, { 0, 1, 0 }, { 0, 0, 0 } };
            this.Factor = 1;
            this.Offset = 0;
        }

        // Normalizes the matrix
        public void Normalize()
        {
            this.Factor = 0;

            foreach (int i in this.Matrix)
                this.Factor += i;

            if (this.Factor == 0)
                this.Factor = 1;
        }

        public override void Process(byte[] source, IntPtr destination, MediaInfo videoInfo)
        {
            int stride = videoInfo.MediaStride;
            int stride2 = videoInfo.MediaStride * 2;

            int videoWidth;
            int videoHeight;
            if (this.Complete)
            {
                videoWidth = videoInfo.MediaWidth;
                videoHeight = videoInfo.MediaHeight;
            }
            else {
                videoWidth = Math.Abs(this.Xmax - this.Xmin);
                videoHeight = Math.Abs(this.Ymax - this.Ymin);
            }

            unsafe
            {
                fixed (byte* pSrcX = (byte*)&source[0])
                {
                    byte* p = (byte*)(void*)destination;
                    byte* pSrc = pSrcX;

                    int nOffset = stride - videoWidth * 3;
                    int nWidth = videoWidth - 2;
                    int nHeight = videoHeight - 2;

                    int nPixel;

                    int y, x;

                    if (!this.Complete)
                    {
                        int xmin = Math.Min(this.Xmax, this.Xmin);
                        int ymin = Math.Min(this.Ymax, this.Ymin);
                        p += ymin * stride + xmin * 3;
                        pSrc += ymin * stride + xmin * 3;
                    }

                    for (y = 0; y < nHeight; ++y)
                    {
                        for (x = 0; x < nWidth; ++x)
                        {
                            nPixel = ((((pSrc[2] * this.Matrix[0, 0]) +
                                (pSrc[5] * this.Matrix[0, 1]) +
                                (pSrc[8] * this.Matrix[0, 2]) +
                                (pSrc[2 + stride] * this.Matrix[1, 0]) +
                                (pSrc[5 + stride] * this.Matrix[1, 1]) +
                                (pSrc[8 + stride] * this.Matrix[1, 2]) +
                                (pSrc[2 + stride2] * this.Matrix[2, 0]) +
                                (pSrc[5 + stride2] * this.Matrix[2, 1]) +
                                (pSrc[8 + stride2] * this.Matrix[2, 2]))
                                / this.Factor));

                            if (nPixel < 0) nPixel = 0;
                            if (nPixel > 255) nPixel = 255;
                            p[5 + stride] = (byte)nPixel;

                            nPixel = ((((pSrc[1] * this.Matrix[0, 0]) +
                                (pSrc[4] * this.Matrix[0, 1]) +
                                (pSrc[7] * this.Matrix[0, 2]) +
                                (pSrc[1 + stride] * this.Matrix[1, 0]) +
                                (pSrc[4 + stride] * this.Matrix[1, 1]) +
                                (pSrc[7 + stride] * this.Matrix[1, 2]) +
                                (pSrc[1 + stride2] * this.Matrix[2, 0]) +
                                (pSrc[4 + stride2] * this.Matrix[2, 1]) +
                                (pSrc[7 + stride2] * this.Matrix[2, 2]))
                                / this.Factor));

                            if (nPixel < 0) nPixel = 0;
                            if (nPixel > 255) nPixel = 255;
                            p[4 + stride] = (byte)nPixel;

                            nPixel = ((((pSrc[0] * this.Matrix[0, 0]) +
                                           (pSrc[3] * this.Matrix[0, 1]) +
                                           (pSrc[6] * this.Matrix[0, 2]) +
                                           (pSrc[0 + stride] * this.Matrix[1, 0]) +
                                           (pSrc[3 + stride] * this.Matrix[1, 1]) +
                                           (pSrc[6 + stride] * this.Matrix[1, 2]) +
                                           (pSrc[0 + stride2] * this.Matrix[2, 0]) +
                                           (pSrc[3 + stride2] * this.Matrix[2, 1]) +
                                           (pSrc[6 + stride2] * this.Matrix[2, 2]))
                                / this.Factor));

                            if (nPixel < 0) nPixel = 0;
                            if (nPixel > 255) nPixel = 255;
                            p[3 + stride] = (byte)nPixel;

                            p += 3;
                            pSrc += 3;
                        }

                        p += (nOffset +6);
                        pSrc += (nOffset +6);
                    }
                }
            }
        }

        //Filter Matrix
        public int[,] Matrix;

        //Filter Factor
        public int Factor;

        //Filter Offset
        public int Offset;
    }
}
